#include <basecode/glfwapp.h>

#include "fluid.h"
#include "basecode/misc_utils.h"
#include <gl\SOIL.h>

class MyApp : public GLFWApp
{
private:
	FluidSolver *solver;

public:
	MyApp() : GLFWApp()
	{
		init();
		mainloop();
	}

	~MyApp() //: ~GLFWApp();
	{
		delete solver;
	}

	void init()
	{
		solver = new FluidSolver();
		physicsFps = 0;
		onePhySec = 0.0;
		physicsFrames = 0;
		lastsimulate = 0.0;
		phys_millis = 0.0,

		renderFps = 0;
		oneRenderSec = 0.0;
		renderFrames = 0;

		total_phys_frames = 0;



		// Init Point Sprite
#define use_point_sprite
#ifdef use_point_sprite
		float sizes[2];
		glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, sizes);
		glEnable( GL_POINT_SPRITE );
		glPointParameterfARB( GL_POINT_SIZE_MAX_ARB, sizes[1] );
		glPointParameterfARB( GL_POINT_SIZE_MIN_ARB, sizes[0]);
		
		float quadratic[] =  { 0.0f, 0.0f, 0.00001f };
		glPointParameterfvARB( GL_POINT_DISTANCE_ATTENUATION_ARB, quadratic );
		glTexEnvi( GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE ); //Generates tex coords for Point 	

		glActiveTexture(GL_TEXTURE0);
		glEnable( GL_TEXTURE_2D );

		int image_width, image_height, channels;
		unsigned char* image_data = SOIL_load_image( "particle.png", &image_width, &image_height, &channels, SOIL_LOAD_RGBA);
		if( image_data != NULL ){
			glEnable(GL_TEXTURE_2D);
			glGenTextures(1, &g_textureID);
			//glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
			glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
			glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR );
			glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR );
			glTexImage2D(GL_TEXTURE_2D, 0, 4, image_width, image_height, 0,
				GL_RGBA, GL_UNSIGNED_BYTE, image_data);
			SOIL_free_image_data(image_data);
		}else{
			printf("couldn't load img\n");
		}
#endif

	}

	void update()
	{
		
		static double desiredFrameTime = 1.0 / 100.0;

		if( Input::MouseDown(GLFW_MOUSE_BUTTON_1) )
		{
			solver->activateAttractor( Input::mouseX(), Input::mouseY() );
		} else if ( !Input::MouseDown(GLFW_MOUSE_BUTTON_1) && solver->attracting )
		{
			solver->deactivateAttractor();
		}

		if( Input::MouseDown(GLFW_MOUSE_BUTTON_2) )
		{
			solver->activateRepelling( Input::mouseX(), Input::mouseY() );
		} else if ( !Input::MouseDown(GLFW_MOUSE_BUTTON_2) && solver->repelling )
		{
			solver->deactivateAttractor();
		}
		
		static double cooldown = 0;
		cooldown -= dt;
		
		bool canAddParticle = Input::keyDown(GLFW_KEY_SPACE) && cooldown <= 0.0;
		if (canAddParticle)
		{
			double particlesPerSecond = 1.0f / 500.0;
			cooldown = particlesPerSecond;
			solver->addParticles( Input::mouseX(), Input::mouseY() );
		}
		
		if (Input::keyDown(GLFW_KEY_DEL)){ solver->removeAllParticles(); }
		if (Input::keyDown('R')){ solver->initParticles(); }

		//if (Input::keyDown('I')) { solver->reinstallShader(); }

		if (Input::keyHit('T')) { 
			solver->do_viscosity_step = !solver->do_viscosity_step; 
			printf("viscosity toggled %i\n", solver->do_viscosity_step);
		}


		double time_before_update = glfwGetTime();
		bool time_to_update = (time_before_update - lastsimulate) > desiredFrameTime;
		if (time_to_update) {			
			
			solver->update(); 
			double time_after = glfwGetTime();
			lastsimulate = time_before_update;
			phys_millis = time_after - time_before_update;

			if( time_before_update > onePhySec )
			{
				physicsFps = physicsFrames;
				physicsFrames = 0;
				onePhySec = time_before_update + 1.0;
			}
			physicsFrames++;
			
			total_phys_frames++;
		}
	}
	
	void render()
	{
		static double lastrender = 0;
		static double desiredFrameTime = 1.0 / 60.0;

		double now = glfwGetTime();
		bool timetorender = (now - lastrender) > desiredFrameTime;
		if( timetorender )
		{
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
			glfwSwapInterval(0);
			//setDepthProjection();
			// Set the projection matrix to a normal frustum with a max depth of 500
			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();
			float aspect_ratio = ((float)xres) / yres;
			//glFrustum(.5, -.5, -.5 * aspect_ratio, .5 * aspect_ratio, 1, 500);
			glusPerspective(45.0, aspect_ratio, 10, 3000.0);
			glMatrixMode(GL_MODELVIEW);

			glLoadIdentity();

			float SIM_W = 300;

			
			float mx_angle = Input::mouseX() / (float)xres;
			//static float mx_angle = 0;
			static int numframes = 0;
			//mx_angle += 0.001f;
			float my_angle = Input::mouseY() / (float)yres;

			//float my_angle = 0.75f;

			glTranslatef(0.0f, 0.0f, -SIM_W*1.7f);
			glRotatef( 180.0f + mx_angle * 180.0f, 0, 1, 0 );
			glRotatef( -90.0f + -my_angle*360.0f, 0, 0, 1 );
			glTranslatef( -SIM_W*0.5f, -SIM_W*0.5f, -SIM_W*0.5f);

			float rot = my_angle * 6.28f;
			solver->G.x = cos( rot );
			solver->G.y = sin( rot );
			solver->G.z = 0;

			solver->render();

			char title[256];
			//char* all_systems_go = " | phyfps>199 :)";
			//char* cant_keep_up = " | pc can't keep up with sim phyfps<199";
			//char *msg = NULL;
			//if( physicsFps > 199 ){
			//	msg = all_systems_go;
			//}else{
			//	msg = cant_keep_up;
			//}

			sprintf_s(title, 256, "physics ms: %.3f, phyfps = %i, particles = %i, upload: %f", phys_millis*1000, physicsFps, solver->getParticleCount(), solver->uploadTime*1000);
			glfwSetWindowTitle( title );

			lastrender = glfwGetTime();
			glfwSwapBuffers();

			static int shotnum = 0;
			
			//if( numframes > 10 && total_phys_frames % 30 == 0 )
			//{
			//	char filename[200];
			//	sprintf_s(filename, 256, "shots/%i.bmp", shotnum );
			//	if( SOIL_save_screenshot(filename, SOIL_SAVE_TYPE_BMP, 0, 0, xres, yres) == 0 )
			//	{
			//		bool bail = true;
			//		gameRunning = false;
			//	}
			//	shotnum++;
			//}

			if( now > oneRenderSec )
			{
				renderFps = renderFrames;
				renderFrames = 0;
				oneRenderSec = now + 1.0;
			}
			renderFrames++;
			numframes++;
		}


	}

private:
	int physicsFps;
	double onePhySec;
	int physicsFrames;
	double lastsimulate, phys_millis;

	int total_phys_frames;

	int renderFps;
	double oneRenderSec;
	int renderFrames;

	GLuint g_textureID;
};